/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.replica.replicaisland;

import com.replica.replicaisland.GameObject.ActionType;

/**
 * A component that allows an object to spawn other objects and apply velocity
 * to them at specific intervals. Can be used to launch projectiles, particle
 * effects, or any other type of game object.
 */
public class LaunchProjectileComponent extends GameComponent {
	private GameObjectFactory.GameObjectType mObjectTypeToSpawn;
	private float mOffsetX;
	private float mOffsetY;
	private float mVelocityX;
	private float mVelocityY;
	private float mThetaError;
	private ActionType mRequiredAction;
	private float mDelayBetweenShots;
	private int mProjectilesInSet;
	private float mDelayBetweenSets;
	private int mSetsPerActivation;
	private float mDelayBeforeFirstSet;

	private float mLastProjectileTime;
	private float mSetStartedTime;
	private int mLaunchedCount;
	private int mSetCount;

	private boolean mTrackProjectiles;
	private int mMaxTrackedProjectiles;
	private int mTrackedProjectileCount;

	private Vector2 mWorkingVector;

	private SoundSystem.Sound mShootSound;

	public LaunchProjectileComponent() {
		super();
		setPhase(ComponentPhases.POST_COLLISION.ordinal());
		mWorkingVector = new Vector2();
		reset();
	}

	@Override
	public void reset() {
		mRequiredAction = ActionType.INVALID;
		mObjectTypeToSpawn = GameObjectFactory.GameObjectType.INVALID;
		mOffsetX = 0.0f;
		mOffsetY = 0.0f;
		mVelocityX = 0.0f;
		mVelocityY = 0.0f;
		mDelayBetweenShots = 0.0f;
		mProjectilesInSet = 0;
		mDelayBetweenSets = 0.0f;
		mLastProjectileTime = 0.0f;
		mSetStartedTime = -1.0f;
		mLaunchedCount = 0;
		mSetCount = 0;
		mSetsPerActivation = -1;
		mProjectilesInSet = 0;
		mDelayBeforeFirstSet = 0.0f;
		mTrackProjectiles = false;
		mMaxTrackedProjectiles = 0;
		mTrackedProjectileCount = 0;
		mThetaError = 0.0f;
		mShootSound = null;
	}

	@Override
	public void update(float timeDelta, BaseObject parent) {
		GameObject parentObject = (GameObject) parent;

		final TimeSystem time = sSystemRegistry.timeSystem;
		final float gameTime = time.getGameTime();

		if (mTrackedProjectileCount < mMaxTrackedProjectiles
				|| !mTrackProjectiles) {
			if (parentObject.getCurrentAction() == mRequiredAction
					|| mRequiredAction == ActionType.INVALID) {

				if (mSetStartedTime == -1.0f) {
					mLaunchedCount = 0;
					mLastProjectileTime = 0.0f;
					mSetStartedTime = gameTime;
				}

				final float setDelay = mSetCount > 0 ? mDelayBetweenSets
						: mDelayBeforeFirstSet;

				if (gameTime - mSetStartedTime >= setDelay
						&& (mSetCount < mSetsPerActivation || mSetsPerActivation == -1)) {
					// We can start shooting.
					final float timeSinceLastShot = gameTime
							- mLastProjectileTime;

					if (timeSinceLastShot >= mDelayBetweenShots) {

						launch(parentObject);
						mLastProjectileTime = gameTime;

						if (mLaunchedCount >= mProjectilesInSet
								&& mProjectilesInSet > 0) {
							mSetStartedTime = -1.0f;
							mSetCount++;
						}
					}
				}
			} else {
				// Force the timer to start counting when the right action is
				// activated.
				mSetStartedTime = -1.0f;
				mSetCount = 0;
			}
		}
	}

	private void launch(GameObject parentObject) {
		mLaunchedCount++;
		GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
		GameObjectManager manager = sSystemRegistry.gameObjectManager;
		if (factory != null && manager != null) {
			float offsetX = mOffsetX;
			float offsetY = mOffsetY;
			boolean flip = false;
			if (parentObject.facingDirection.x < 0.0f) {
				offsetX = parentObject.width - mOffsetX;
				flip = true;
			}

			if (parentObject.facingDirection.y < 0.0f) {
				offsetY = parentObject.height - mOffsetY;
			}

			final float x = parentObject.getPosition().x + offsetX;
			final float y = parentObject.getPosition().y + offsetY;
			GameObject object = factory.spawn(mObjectTypeToSpawn, x, y, flip);
			if (object != null) {
				mWorkingVector.set(1.0f, 1.0f);
				if (mThetaError > 0.0f) {
					final float angle = (float) (Math.random() * mThetaError
							* Math.PI * 2.0f);
					mWorkingVector.x = (float) Math.sin(angle);
					mWorkingVector.y = (float) Math.cos(angle);
					if (Utils.close(mWorkingVector.length2(), 0.0f)) {
						mWorkingVector.set(1.0f, 1.0f);
					}
				}
				mWorkingVector.x *= flip ? -mVelocityX : mVelocityX;
				mWorkingVector.y *= mVelocityY;

				object.getVelocity().set(mWorkingVector);
				object.getTargetVelocity().set(mWorkingVector);
				// Center the projectile on the spawn point.
				object.getPosition().x -= object.width / 2.0f;
				object.getPosition().y -= object.height / 2.0f;

				if (mTrackProjectiles) {
					object.commitUpdates();
					LifetimeComponent projectileLife = object
							.findByClass(LifetimeComponent.class);
					if (projectileLife != null) {
						projectileLife.setTrackingSpawner(this);
						mTrackedProjectileCount++;
					}
				}
				manager.add(object);

				if (mShootSound != null) {
					SoundSystem sound = sSystemRegistry.soundSystem;
					if (sound != null) {
						sound.play(mShootSound, false,
								SoundSystem.PRIORITY_NORMAL);
					}
				}
			}
		}

	}

	public final void setObjectTypeToSpawn(
			GameObjectFactory.GameObjectType objectTypeToSpawn) {
		mObjectTypeToSpawn = objectTypeToSpawn;
	}

	public final void setOffsetX(float offsetX) {
		mOffsetX = offsetX;
	}

	public final void setOffsetY(float offsetY) {
		mOffsetY = offsetY;
	}

	public final void setVelocityX(float velocityX) {
		mVelocityX = velocityX;
	}

	public final void setVelocityY(float velocityY) {
		mVelocityY = velocityY;
	}

	public final void setRequiredAction(ActionType requiredAction) {
		mRequiredAction = requiredAction;
	}

	public final void setDelayBetweenShots(float launchDelay) {
		mDelayBetweenShots = launchDelay;
	}

	public final void setDelayBetweenSets(float delayBetweenSets) {
		mDelayBetweenSets = delayBetweenSets;
	}

	public final void setDelayBeforeFirstSet(float delayBeforeFirstSet) {
		mDelayBeforeFirstSet = delayBeforeFirstSet;
	}

	public final void setShotsPerSet(int shotCount) {
		mProjectilesInSet = shotCount;
	}

	public final void setSetsPerActivation(int setCount) {
		mSetsPerActivation = setCount;
	}

	public final void enableProjectileTracking(int max) {
		mMaxTrackedProjectiles = max;
		mTrackProjectiles = true;
	}

	public final void disableProjectileTracking() {
		mMaxTrackedProjectiles = 0;
		mTrackProjectiles = false;
	}

	public final void trackedProjectileDestroyed() {
		assert mTrackProjectiles;
		if (mTrackedProjectileCount == mMaxTrackedProjectiles) {
			// Let's restart the set.
			mSetStartedTime = -1.0f;
			mSetCount = 0;
		}
		mTrackedProjectileCount--;
	}

	public final void setThetaError(float error) {
		mThetaError = error;
	}

	public final void setShootSound(SoundSystem.Sound shoot) {
		mShootSound = shoot;
	}

}
